package cn.boodqian.airreport;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import cn.boodqian.util.Log;
import android.view.View;

public class HistoryView extends View {
	private Paint mPaint;
	private float mStartX, mEndX;
	private float mStartY, mEndY;
	private float mIndicatorWidth = 5f;
	private float mPointRadius = 2.5f;
	private float mLineWidth = 2f;
	private float mAQITextWidth;
	private float mHeightPerLevel;
	private float mColorIndicatorHeight;
	private float mColorIndicatorWidth = 12f;
	private float mColorIndicatorGapX = 5f;
	private float mColorIndicatorGapY = 4f;
	private float mHorizonMarkHeight;
	
	private boolean mSelfAdapt = false;
	private ArrayList<List<Float>> mData = new ArrayList();
	private ArrayList<Integer> mDataColors = new ArrayList();
	private ArrayList<String> mDataNames = new ArrayList();
	private List<String> mHorizonMarks = null;;
	
	int [] colors = {Color.parseColor("#00E400"), Color.YELLOW, Color.parseColor("#FF7E00"), Color.RED,
			Color.parseColor("#7E0023"), Color.parseColor("#99004C"), Color.parseColor("#99004C"), Color.parseColor("#99004C")};
	int [] bkcolors = {0xff444444, 0xff666666};
	
	private static final String TAG = "HistoryView";
	
	public HistoryView(Context context) {
		super(context);
		initHistoryView();
	}
	
	public HistoryView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initHistoryView();
	} 
	
	public void clearHistoryData() {
		mData.clear();
		mDataColors.clear();
		mDataNames.clear();
		mHorizonMarks = null;
	}
	
	// Must be called before addHistoryData
	public void setHorizonMark(List<String> marks)
	{
		assert(mData.isEmpty());
		
		mHorizonMarks = marks;
	}
	
	public void addHistoryData(List<Float> data, int color, String name) {
		assert(mHorizonMarks == null || data.size()==mHorizonMarks.size() );
		
		mData.add(data);
		mDataColors.add(color);
		mDataNames.add(name);
	}
	
	private void initHistoryView()
	{
		mPaint = new Paint();
		setPadding(5,5,5,5);
	}
	
	private int getMaxAQILevel() {
		float max_aqi = 0;
		for(int i=0;i<mData.size();i++) {
			List<Float> data = mData.get(i);
			for(int j=0;j<data.size();j++)
				if(data.get(j) > max_aqi) max_aqi = data.get(j);
		}
		
		int max_level = AQI.AQILevel(Math.round(max_aqi));
		if(!mSelfAdapt) max_level=6;
		return max_level;
	}
	
	private void drawHorizonMarks(Canvas canvas) {
		if(mHorizonMarks == null) return;
		if(mHorizonMarks.size() == 0) return;
		
		int length = mHorizonMarks.size();
		float width_per_point = (mEndX - mStartX)/length;
		float x = mStartX + mIndicatorWidth, y = mEndY + mHorizonMarkHeight;
		mPaint.reset();
		mPaint.setColor(Color.WHITE);
		mPaint.setAntiAlias(true);
		for(int i=0;i<length;i++) {
			if(i%3==0 || i==length-1) {
				float markWidth =  mPaint.measureText(mHorizonMarks.get(i));
				canvas.drawText(mHorizonMarks.get(i), x - markWidth/2 - mPointRadius, y, mPaint);
			}
			x += width_per_point;
		}
		
		x = mStartX + mIndicatorWidth;
		mPaint.reset();
		mPaint.setColor(Color.GRAY);
		mPaint.setStyle(Style.STROKE);
		mPaint.setPathEffect(new DashPathEffect(new float[] {2,10,2,10}, 0));
		for(int i=0;i<length;i++) {
			if(i%3==0) {
				if(i>0) {
					canvas.drawLine(x - mPointRadius, mStartY, x - mPointRadius, mEndY, mPaint);
				}
			}
			x += width_per_point;
		}
	}
	
	private void drawData(int index, Canvas canvas) {
		List<Float> data = mData.get(index);
		int color = mDataColors.get(index);
		int length = data.size();
		if(length==0) return;
		
		float width_per_point = (mEndX - mStartX)/length;
		if(Log.isLoggable(Log.VERBOSE))  Log.v(String.format("width_per_hour=%.3f", width_per_point));
		
		float [] x = new float[length];
		float [] y = new float[length];
		for(int i=0;i<length;i++) {
			x[i] = mStartX + width_per_point*i;
			if(data.get(i)>500) y[i] = mEndY;
			else if(data.get(i)>300) y[i] = mEndY - ((data.get(i)-300)/200f+5)*mHeightPerLevel;
			else if(data.get(i)>200) y[i] = mEndY - ((data.get(i)-200)/100f+4)*mHeightPerLevel;
			else y[i] = mEndY - data.get(i)/50f*mHeightPerLevel;
			
			// The if here is important for performance
			if(Log.isLoggable(Log.VERBOSE))  Log.v(String.format("%s=%f, (%.3f,%.3f)", mDataNames.get(index), data.get(i), x[i], y[i]));
		}
		
		mPaint.reset();
		mPaint.setStrokeWidth(mLineWidth);
		for(int i=1;i<x.length;i++) {
			mPaint.setColor(color);
			if(data.get(i).compareTo(0f)!=0) {
				if(data.get(i-1).compareTo(0f)!=0)
					canvas.drawLine(x[i-1]+mPointRadius, y[i-1], x[i], y[i], mPaint); /* Add mPointRadius to avoid overlapping the AQI indicator */
				mPaint.setColor(Color.WHITE);
				canvas.drawCircle(x[i]+mPointRadius, y[i], mPointRadius, mPaint);
			}
		}
		if(data.get(0).compareTo(0f)!=0) {
			mPaint.setColor(Color.WHITE);
			canvas.drawCircle(x[0]+mPointRadius, y[0], mPointRadius, mPaint);
		}
	}
	
	@Override
	protected void onDraw(Canvas canvas) {
		mHorizonMarkHeight = mPaint.descent() - mPaint.ascent();
		mAQITextWidth = mPaint.measureText("000");
		mColorIndicatorHeight = mPaint.descent() - mPaint.ascent();
		mStartX = getPaddingLeft() + mAQITextWidth + mIndicatorWidth;
		mEndX = getWidth() - getPaddingRight();
		mStartY = getPaddingTop();
		mEndY = getHeight() - mHorizonMarkHeight - mColorIndicatorHeight - mColorIndicatorGapY - getPaddingBottom();
		
		if(Log.isLoggable(Log.VERBOSE))  Log.v(String.format("x=%.3f-%.3f, y=%.3f-%.3f", mStartX, mEndX, mStartY, mEndY));
		
		int max_level = getMaxAQILevel();
		
		mHeightPerLevel = (mEndY - mStartY)/max_level;
		if(Log.isLoggable(Log.VERBOSE))  Log.v(String.format("maxlevel=%d,mHeightPerLevel=%.3f", max_level, mHeightPerLevel));
		
		mPaint.reset();
		mPaint.setColor(Color.WHITE);
		mPaint.setAntiAlias(true);
		for(int i=0;i<=max_level;i++) {
			int aqi = 50*i;
			if(i==5) aqi = 300;
			else if(i>5) aqi = 500;
			canvas.drawText(Integer.toString(aqi), getPaddingLeft(), mEndY-i*mHeightPerLevel + Math.abs(mPaint.descent() + mPaint.ascent())/2, mPaint);
		}
		
		mPaint.reset();
		mPaint.setStrokeWidth(mIndicatorWidth);

		for(int i=0;i<max_level;i++) {
			mPaint.setColor(colors[i]);
			canvas.drawLine(mStartX-mIndicatorWidth/2, mEndY-i*mHeightPerLevel, mStartX-mIndicatorWidth/2, mEndY-(i+1)*mHeightPerLevel, mPaint);
			mPaint.setColor(bkcolors[i%bkcolors.length]);
			canvas.drawRect(mStartX, mEndY-(i+1)*mHeightPerLevel, mEndX,  mEndY-i*mHeightPerLevel, mPaint);
		}
		
		// Color Indicator
		mPaint.reset();
		mPaint.setAntiAlias(true);
		float x = mEndX, y = getHeight() - getPaddingBottom();
		for(int i=0;i<mData.size();i++) {
			float txtWidth = mPaint.measureText(mDataNames.get(i));
			x -= txtWidth +mColorIndicatorGapX;
			mPaint.setColor(Color.WHITE);
			canvas.drawText(mDataNames.get(i), x, y, mPaint);
			
			x -= mColorIndicatorWidth + mColorIndicatorGapX;
			mPaint.setColor(mDataColors.get(i));
			canvas.drawRect(x, y-mColorIndicatorHeight, x+mColorIndicatorWidth, y, mPaint);
		}
		
		for(int i=0;i<mData.size();i++)
			drawData(i, canvas);
		
		drawHorizonMarks(canvas);
		
		super.onDraw(canvas);
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		setMeasuredDimension(measureWidth(widthMeasureSpec),
                measureHeight(heightMeasureSpec));
	}

    /**
     * Determines the width of this view
     * @param measureSpec A measureSpec packed into an int
     * @return The width of the view, honoring constraints from measureSpec
     */
    private int measureWidth(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            // We were told how big to be
            result = specSize;
        } else {
        	// TODO Fix here
            // Measure the text
            result = (int) mPaint.measureText("000") /* AQI value */ + getPaddingLeft()
                    + getPaddingRight();
            if (specMode == MeasureSpec.AT_MOST) {
                // Respect AT_MOST value if that was what is called for by measureSpec
                result = Math.min(result, specSize);
            }
        }

        if(Log.isLoggable(Log.DEBUG)) Log.d("width:"+result);
        return result;
    }

    /**
     * Determines the height of this view
     * @param measureSpec A measureSpec packed into an int
     * @return The height of the view, honoring constraints from measureSpec
     */
    private int measureHeight(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            // We were told how big to be
            result = specSize;
        } else {
        	// TODO fix here
            // Measure the text (beware: ascent is a negative number)
            result = (int) (-mPaint.ascent() + mPaint.descent()) + getPaddingTop()
                    + getPaddingBottom();
            if (specMode == MeasureSpec.AT_MOST) {
                // Respect AT_MOST value if that was what is called for by measureSpec
                result = Math.min(result, specSize);
            }
        }
        if(Log.isLoggable(Log.DEBUG)) Log.d("height:"+result);
        return result;
    }
}
